"""
@username : users
@author : FanMY_71
@projectname: myflask
@Time: 2022/8/25-10:07
"""
from werkzeug.security import generate_password_hash

from libs.error_code import SexException
from model import db
import datetime

# 用户关注表中间表   自连接
tb_attention = db.Table(
    "tb_attention",
    db.Column("attention_id", db.Integer, db.ForeignKey("users.user_id")),  # 你关注人的id
    db.Column("fans_id", db.Integer, db.ForeignKey("users.user_id"))  # 粉丝的id
)


class Users(db.Model):
    __tablename__ = "users"  # 映射的数据表名，不写的话，默认是映射成和类名一样的表名
    # 设计数据库的映射字段
    user_id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    user_CreateAt = db.Column(db.DATETIME, default=datetime.datetime.now)
    user_UpdateAt = db.Column(db.DATETIME, default=datetime.datetime.now, onupdate=datetime.datetime.now)
    user_DeleteBit = db.Column(db.Enum("1"), default=None)

    username = db.Column(db.String(128), nullable=False)
    # 把password变成_password，然后映射到数据库里的password
    _password = db.Column("password", db.String(512), nullable=False)
    mobile = db.Column(db.String(24), nullable=False, unique=True)
    email = db.Column(db.String(128), nullable=False, unique=True)

    sex = db.Column(db.Enum("F", "M", "S"), default="S")
    role = db.Column(db.Enum("Y", "N", ), default="N")

    # 建立多对多关系，用户和粉丝
    fans_id = db.relationship("Users",
                              secondary=tb_attention,
                              primaryjoin=(tb_attention.c.fans_id == user_id),
                              secondaryjoin=(tb_attention.c.attention_id == user_id),
                              backref=db.backref("attention_id", lazy="dynamic"),
                              lazy="dynamic"
                              )
    """
    这里followers对应的就是fans_id。followerd对应的就是attention_id
    User 是关系的右侧实体（左侧是父类）。由于这是一种自引用关系，我必须在两边使用相同的类。
    secondary 配置用于这个关系的关联表，就是在这个类上定义的关联表 followers。
    primaryjoin指定了左侧实体（关注者）与关联表链接的条件。关系左侧的连接条件是与关联表中follower_id字段匹配的用户ID。follwer.c.follower_id表达式引用了关联表中follower_id列。
    secondaryjoin指定了右侧实体（被关注者）与关联表链接的条件。这个条件与primaryjoin类似，唯一不同的是：现在使用的followed_id，它是关联表中的另一个外键。
    backref定义如何右侧实体访问这个关系。从左侧开始，关系被名称为 followed，因此右侧将使用名称followers 来表示链接到右侧目标用户的所有左侧用户。附加lazy 参数表示这个查询的执行模式，设置为dynamic模式的查询在特定请求之前不会运行，这也是我们设置帖子的一对多关系。
    lazy类似于同名参数backref，但这个适用于左侧查询而不是右侧。
    """

    # 建立一对多关系，管理员和普通用户
    admin_id = db.Column(db.Integer,db.ForeignKey("users.user_id"))
    noruser = db.relationship('Users', back_populates='administrator')   # 普通用户
    administrator = db.relationship('Users', back_populates='noruser', remote_side=[user_id])  # 管理员

    def __str__(self):
        return self.name

    def keys(self):  # 改写dict的keys，定义返回的数据
        return "id", 'username', 'password', 'email', 'phone', 'role', 'CreateAt'

    def __getitem__(self, item):
        return getattr(self, item)

    def to_json(self):
        return {
            "username": self.username,
            "CreateAt": self.user_CreateAt,
            "mobile": self.mobile,
            "email": self.email,
            "sex": self.sex
        }

    # 属性包装   定义一些属性之前做一些检测，转换
    @property  # -----> password.setter  生成两个装饰器：一个叫做"被包装的函数名.setter" 一个叫做被包装的函数名.deleter"
    def password(self):
        return self._password

    # 把user.password()变成了一个属性user.password
    # 我们可以print(user.password)或者进行赋值
    # user.paassword = xxx 进行赋值的时候 会自动执行被password.setter包装的函数
    @password.setter
    def password(self, value):  # value就是xxx
        self._password = generate_password_hash(value)  #

    @classmethod  # classmethod修饰符对应的函数不需要实例化
    def create_user(cls, username, password, email, mobile, sex):
        user = cls()
        user.username = username
        user.password = password  # 执行这个的时候，会自动执行被.setter装饰的函数
        user.email = email
        user.mobile = mobile
        if sex in ["F", "M", "S"]:
            user.sex = sex
            print(user.sex)
            db.session.add(user)
            print("2:", user.sex)
            db.session.commit()
            print("3:", user.sex)
            print(user.username)
        else:
            raise SexException


#API授权表的模型
#多对多的关系
app_permission = db.Table("app_permission",
                          db.Column("api_id",db.ForeignKey("api_token.id")),
                          db.Column("permission_id",db.ForeignKey("api_permission.id"))
                          )
# api_token表
#存放的是授权密钥，以及授权id
class ApiToken(db.Model):
    __tablename__ = "api_token"
    id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    appid = db.Column(db.String(128), nullable=False)
    secretkey = db.Column(db.String(128), nullable=False)
    #通过中间表去创建多对多的关系
    manage = db.relationship("ApiPermission", secondary=app_permission, backref="token")

#存放的是授权的url
class ApiPermission(db.Model):
    __tablename__ = "api_permission"
    id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    url = db.Column(db.String(128), nullable=False)
    method_type = db.Column(db.String(128), nullable=False)

#db.relationship()
# 第一个参数表示这个关系的另一端是哪个模型.
# secondaty 中间表
# backref 在ApiPermission模型添加了一个token属性, 作为反向关系.可用这一属性访问 ApiToken 模型对象
